package com.wang.tool.valve;

import com.alibaba.fastjson.JSONObject;
import com.wang.tool.monitor.LinkLogUtil;
import com.wang.tool.monitor.core.MonitorInfo;
import com.wang.tool.util.CommonUtil;

/**
 * @Description 阀门策略代理，无缝切入策略适配器
 * 利用json的key-val切入ValveAdapter适配器,可以理解为Valve的代理,适配所有策略
 * 具体参数填充,可完全按照适配器来进行,通过修改内部参数,实现动态嫁接不同策略
 * 附带name和log提供常需的命名和日志功能
 * 附带open大开关
 * @Author shaopeng
 * @Date 2021/8/5
 * @Version 1.0
 */
public class ValveAgent extends JSONObject implements Valve {
    private static final long serialVersionUID = 6304669723227417403L;


    /**
     * log
     * isPass 打印执行日志
     */
    private static final String KEY_LOG = "log";

    /**
     * 是否开启 true/false
     */
    private static final String KEY_OPEN = "open";

    /**
     * 名字/描述 不序列化
     */
    private String name;

    /**
     * 是否初始化 不序列化
     * 空代表 未解析
     * false 代表无效
     * true 代表有效
     */
    private volatile Boolean init;

    /**
     * 是否准备好 不序列化
     * 空代表 未尝试
     * false 代表无效
     * true 代表有效
     */
    private volatile Boolean prepare;

    /**
     * 代理  不序列化
     */
    private Valve delegate;


    /**
     * 构建开启的agent
     *
     * @return
     */
    public static ValveAgent buildOpen(String name) {
        ValveAgent agent = new ValveAgent();
        agent.setOpen(true);
        agent.setName(name);
        return agent;
    }

    /**
     * 构建关闭的agent
     *
     * @return
     */
    public static ValveAgent buildClose(String name) {
        ValveAgent agent = new ValveAgent();
        agent.setOpen(false);
        agent.setName(name);
        return agent;
    }

    /**
     * 新Valve替换老Valve
     * 如果新阀门有效则返回新阀门,其余返回老阀门
     *
     * @param newValveStr 新阀门
     * @param oldValve    老阀门
     * @return
     */
    public static ValveAgent tryReplace(String newValveStr, ValveAgent oldValve) {
        ValveAgent newValve = CommonUtil.isBlank(newValveStr) ? new ValveAgent() : JSONObject.parseObject(newValveStr, ValveAgent.class);
        return tryReplace(newValve, oldValve);
    }

    /**
     * 新Valve替换老Valve
     * 如果新阀门有效则返回新阀门,其余返回老阀门
     *
     * @param newValve 新阀门
     * @param oldValve 老阀门
     * @return
     */
    public static ValveAgent tryReplace(ValveAgent newValve, ValveAgent oldValve) {
        //不序列化属性拷贝
        if (newValve != null && oldValve != null) {
            newValve.setName(oldValve.getName());
        }
        return (ValveAgent) ValveAdapter.tryReplace(newValve, oldValve);
    }


    /**
     * 测试是否通过
     *
     * @param config 配置字符串
     * @param obj    对象
     * @return
     */
    public static String testIsPass(String config, Object obj) {
        if (CommonUtil.isBlank(config)) {
            return "config is null";
        }
        ValveAgent valve;
        try {
            valve = JSONObject.parseObject(config, ValveAgent.class);
            valve.prepare();
        } catch (Exception e) {
            return "config:" + config + " is not valid, errMsg:" + e.getMessage();
        }
        return String.valueOf(valve.isPass(obj));
    }


    @Override
    public void prepare() throws IllegalArgumentException {
        if (!checkInit()) {
            throw new IllegalArgumentException("config is not valid");
        }
        try {
            delegate.prepare();
            prepare = true;
        } catch (Exception e) {
            prepare = false;
            throw e;
        }
    }


    @Override
    public boolean isPass(Object param) {
        boolean pass = false;
        if (isOpen()) {
            try {
                if (!checkValid()) {
                    LinkLogUtil.warn("[delegateInvalid] name:{0}, param:{1}", getName(), param);
                } else {
                    pass = delegate.isPass(param);
                }
            } catch (Throwable e) {
                LinkLogUtil.newTagLog("isPassError")
                        .v1(getName())
                        .v2(e.getClass().getSimpleName())
                        .addAttachment(MonitorInfo.PARAM, param)
                        .addAttachment(MonitorInfo.ERROR, e)
                        .log();
            }
        }
        if (isLog()) {
            LinkLogUtil.info("[valveIsPass] name:{0}, isPass:{1}, param:{2}", getName(), pass, param);
        }
        return pass;
    }

    /**
     * 刷新配置
     *
     * @return 是否可用
     */
    public synchronized boolean refresh() {
        init = null;
        prepare = null;
        return checkValid();
    }


    /**
     * 是否可用
     *
     * @return
     */
    public boolean checkValid() {
        return checkInit() && checkPrepare();
    }

    private boolean checkInit() {
        if (init != null) {
            return init;
        }
        synchronized (this) {
            if (init != null) {
                return init;
            }
            try {
                //用到的函数都尝试一下
                isLog();
                isOpen();

                //赋值
                delegate = ValveAdapter.parseConfig2Valve(this);
                init = true;
                LinkLogUtil.info("[valveDelegateInitSuccess] name:{0}, delegate:{1}", getName(), delegate);
            } catch (Throwable e) {
                init = false;
                LinkLogUtil.error("valveDelegateInitError", e, "name:{0}, config:{1}", getName(), toJSONString());
            }
            return init;
        }
    }


    /**
     * 是否有效
     *
     * @return
     */
    private boolean checkPrepare() {
        if (prepare != null) {
            return prepare;
        }
        synchronized (this) {
            if (prepare != null) {
                return prepare;
            }
            try {
                delegate.prepare();
                prepare = true;
                LinkLogUtil.info("[valveDelegatePrepareSuccess] name:{0}, delegate:{1}", getName(), delegate);
            } catch (Exception e) {
                prepare = false;
                LinkLogUtil.error("valveDelegatePrepareError", e, "name:{0}", getName());
            }
            return prepare;
        }
    }


    public ValveAgent setName(String name) {
        this.name = name;
        return this;
    }

    public String getName() {
        return name;
    }


    public boolean isLog() {
        return getBooleanValue(KEY_LOG);
    }

    public void setLog(boolean log) {
        put(KEY_LOG, log);
    }

    public boolean isOpen() {
        return getBooleanValue(KEY_OPEN);
    }


    public void setOpen(boolean open) {
        put(KEY_OPEN, open);
    }



    @Override
    public String toString() {
        return "[ValveAgent, name:" + name + ", json:" + toJSONString() + ", delegate:" + delegate + "]";
    }

}
